﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Navigation;
using System.Diagnostics;
using System.Windows.Input;
using System.Dynamic;
using System.ComponentModel;
using System.Reflection;
using System.Windows.Data;
using System.Windows.Controls.Primitives;

namespace ViewMaker.Core.Silverlight
{
    /// <summary>
    /// Silverlightのコントロールの機能を拡張
    /// </summary>
    internal static class SlFrameworkExtension
    {
        /// <summary>
        /// 強制的な入力検証を行う対象のコントロールのプロパティ
        /// </summary>
        internal static Dictionary<Type, DependencyProperty[]> targetProperty;

        static SlFrameworkExtension()
        {
            targetProperty = new Dictionary<Type, DependencyProperty[]>();
            targetProperty.Add(typeof(TextBox), new DependencyProperty[] { TextBox.TextProperty });
            targetProperty.Add(typeof(Selector), new DependencyProperty[] { Selector.SelectedItemProperty, Selector.SelectedValueProperty });
            targetProperty.Add(typeof(ToggleButton), new DependencyProperty[] { ToggleButton.IsCheckedProperty });
            targetProperty.Add(typeof(DatePicker), new DependencyProperty[] { DatePicker.TextProperty, DatePicker.SelectedDateProperty });
        }

        /// <summary>
        /// 指定したコントロールの領域を強制的にUpdateSourceする。これによって検証処理が実行される。
        /// </summary>
        /// <param name="element">検証を行うコントロール</param>
        /// <param name="name">検証する領域。nullを指定すると該当コントロール以下全てが対象になる</param>
        public static void UpdateBindingSource(this FrameworkElement element, string name)
        {
            var ctrl = element as Control;
            if (ctrl != null && !ctrl.IsEnabled) return;

            if (name == null || element.Name == name)
            {
                foreach (var dict in targetProperty)
                {
                    if (dict.Key.IsAssignableFrom(element.GetType()))
                    {
                        foreach (DependencyProperty dp in dict.Value)
                        {
                            var expression = element.GetBindingExpression(dp);
                            if (expression == null) continue;
                            var bind = expression.ParentBinding;
                            if (bind.Mode == BindingMode.OneTime || bind.Mode == BindingMode.OneWay) continue;
                            expression.UpdateSource();
                        }
                    }
                }
                name = null;
            }
            foreach (var child in element.GetVisualChildren())
            {
                var fe = child as FrameworkElement;
                if (fe != null) UpdateBindingSource(fe, name);
            }
        }

        /// <summary>
        /// 指定したコントロールの領域で入力検証エラーが発生しているか。
        /// </summary>
        /// <param name="element">検証を行うコントロール</param>
        /// <param name="name">検証する領域。nullを指定すると該当コントロール以下全てが対象になる</param>
        /// <returns>エラーが発生している場合：true</returns>
        public static bool HasErrorChildren(this FrameworkElement element, string name)
        {
            var ctrl = element as Control;
            if (ctrl != null && !ctrl.IsEnabled) return false;

            bool hasError = false;
            if (name == null || element.Name == name)
            {
                if (Validation.GetHasError(element)) hasError = true;
                name = null;
            }
            foreach (var child in element.GetVisualChildren())
            {
                var fe = child as FrameworkElement;
                if (fe != null && HasErrorChildren(fe, name)) hasError = true;
            }
            return hasError;
        }

        public static FrameworkElement FindById(this FrameworkElement target, string name)
        {
            if (target == null) return null;
            if (target.Name == name) return target;
            foreach (object child in target.GetVisualChildren())
            {
                var fe = child as FrameworkElement;
                var ret = FindById(fe, name);
                if (ret != null) return ret;
            }
            return null;
        }

    }
}
